<?php
/**
 * @description: 
 * oexcel model用来封装了PHPExcel的读取excel文件的表格数据、导出数据到表格的功能
 * 使用方法：
 * $this->load->model('oexcel');
 * $this->load->methodName...
 * 
 * 本模块提供的方法和属性：
 * $conf {array} 私有属性，用来设置要读取的表格文件的开始和结束位置，形成一个区间
 * 
 * setCallback($col,$callbackFun)
 * 为一列设置回调函数。在遍历获取表格内容时将对应调用。参数：列号；回调函数句柄。
 * 回调函数接收一个参数，为单元格的值。
 * 
 * 特例：
 * 当$col参数的值为"row" 时，将为行设置回调函数；回调函数的参数为整行的值。
 * 
 * 注意：所有回调函数都必须返回值，返回的值将作为从excel表读取的内容存放在一个二维数组中返回。如果不返回值，就会使用null或""填充位置。
 * 
 * setCallbacks($arr)
 * 为多列设置回调，一个二维数组，第二维为关联数组键值对为列号=>回调函数。
 * 
 * setStart("A",1)
 * 公共方法，用来设置开始读取的单元格位置
 * 
 * get($filePath,$rows,$cols)
 * 公共方法，主功能，获取从表格中读取的数据并以二维数组的方式返回数据。
 */


class Oexcel extends CI_Model {
	public function __construct(){
		parent::__construct();
		// import class library
		$this->load->library('PHPExcel');
		$this->load->library('PHPExcel/IOFactory.php');
		
	}



	// conf配置参数，私有的
	private $conf = array(
		"maxRow" => 0,
		"maxCol" => 0,
		"startRow" => 1,
		"startCol" => "A"
	);

	// 私有函数变量句柄，保存通过setCallback函数设置的回调函数。以key、value形式，key为列号，value为回调函数
	private $callbackList = array();
	
	
	/**
  * @example: setCallback($col,$callbackFun)
  * @description: 设置一列的回调函数。如果要和setCallbacks一起使用，应该在它之后使用，否则先调用的本方法的设置将被覆盖。当col参数为字符串row时，将为每一行设置回调。
  * @param {string} [col] [列号，大写字母]
  * @param {function} [callbackFun] [对应的回调函数句柄]
  * @return: {object} example: $this
  */
	public function setCallback($col,$callbackFun){
		
			$this->callbackList[$col] = $callbackFun;
		
		return $this;
	}


	/**
  * @example:  setCallbacks(array(array("A"=>function(){})))
  * @description: 批量设置excel列的回调函数。注意如果通过setCallback设置过回调之后调用本方法将覆盖清空之前的所有设置，可以在本方法之后调用setCallback
  * @param {array} [$arr] [一个二维数组，二维数组为关联数组，键为excel表格的列号，值为一个回调的函数。]
  * @return: {object } --- example: $this
  */
	public function setCallbacks($arr){
		$this->callbackList = $arr;
		return $this;
	}


	/**
  * @example: setStartRow(2)
  * @description: 本功能用来设置要读取的行的开始处，默认从第1行开始读取
  * @param {number} [rowNum] [行数的数字索引index]
  * @return: {object} example: $this
  */
	public function setStartRow($rowNum){
		$this->conf['startRow'] = $rowNum;
		return $this;
	}




	/**
  * @example: setStartCol("C")
  * @description: 本功能用来设置要读取的列的开始处，默认从A列开始
  * @param {string} [colNum] [列名称索引，大写字母]
  * @return: {object} example: $this
  */
  public function setStartCol($colNum){
	$this->conf['startCol'] = $colNum;
	return $this;
}


	/**
  * @example: setStart("C",2)
  * @description: 本功能用来设置开始读取的单元格的列，行的索引
  * @param {string} [colNum] [列名称索引，大写字母]
  * @param {string} [rowNum] [行索引，数字]
  * @return: {object} example: $this
  */
  public function setStart($colNum = "A",$rowNum = 1){
	$this->conf['startRow'] =  $rowNum ;
	$this->conf['startCol'] =  $colNum ;
	return $this;
}






	/**
  * @example: get('file.xlxs',0,5);
  * @description: read  a excel file's data
  * @param {string} [$file] [file name]
  * @param {number} [$rows] [read rows limit default is 0 ,will read all row]
  * @param {letter} [$cols] [read cols limit default is 0 ,will read all cols]
  * @return: {array} --- example: [[],[]]
  */
	public function get($file,$rows=0,$cols=0){
		// print_r($this->callback);
		// return;
		// 参数$cols的验证
		if(!is_array($cols)){
			if (!preg_match('/^[A-Z]$/',$cols) && $cols != 0 ){
				die ('参数$cols必须为大写字母或者多个大写字母组成的数组，限定读取数据的最大列或者读取列。');
			}
		}
		// 参数$rows的验证
		if(!is_numeric($rows)){
			die ('参数$rows必须是数字');
		}

		// 开始实例化phpecel类对象
		$reader = new PHPExcel_Reader_Excel2007();   //建立reader对象  

		if(!$reader->canRead($file)){  
	
			$reader = new PHPExcel_Reader_Excel5();  
	
			if(!$reader->canRead($file)){  
	
				die('No Excel!');  
	
			}  
	
		}  

        $PHPExcel = $reader->load($file); // 载入excel文件          
        $sheet = $PHPExcel->getSheet(0); // 读取第一個工作表  
        $highestRow =  $sheet->getHighestRow(); // 取得总行数  
        $highestColumm =  $sheet->getHighestColumn(); // 取得总列数 ,是字母
		
		// 确定要读取的行数和列数
		// 只有rows和cols参数在最大范围之内时，才有作用，否则都是返回的总的行或列
		if($rows>0 && $rows <= $highestRow){
			$highestRow = $rows;
		}
		
		// 当cols不是数组且在列范围内，且不是0，就可以将cols的值用来界定想获取的最大列
		if(!is_array($cols) && $cols >= "A" && $cols <= $highestColumm && $cols != 0){
			$highestColumm = $cols;
		}

        /** 循环读取每个单元格的数据 */  
        $data = array();
        for ($row = $this->conf['startRow']; $row <= $highestRow; $row++){//行数是以第1行开始  
			$dataset = array();
			
			// if $cols is letter
			if(!is_array($cols)){
				for ($column = $this->conf['startCol']; $column <= $highestColumm; $column++) {//列数是以A列开始  
					$val = $sheet->getCell($column.$row)->getValue();
					
					// 如果这一列设置了回调函数,就将单元格的值给回调函数执行之后再返回值
					if(isset($this->callbackList[$column])){
						$val = call_user_func($this->callbackList[$column],$val);
					}
					$dataset[] = $val === NULL ? "" : $val;
					                  
				}
			}else{//$cols is array
				foreach($cols as $item){
					if($item <= $highestColumm){//只有当想获取的列在范围内才行。
						$val = $sheet->getCell($item.$row)->getValue();
						// 如果这一列设置了回调函数,就将单元格的值给回调函数执行之后再返回值
						if(isset($this->callbackList[$column])){
							$val = call_user_func($this->callbackList[$column],$val);
						}
						$dataset[] = $val === NULL ? "" : $val;
					}
				}
			}
			
			$datasetcallback = NULL;
			// 如果行设置了回调函数,就将这一行的值给回调函数执行之后再返回值作为行值
			if(isset($this->callbackList["row"])){
				$datasetcallback = call_user_func($this->callbackList["row"],$dataset);
			}

            $data[] = $datasetcallback === NULL ? $dataset : $datasetcallback;//如果回调的函数忘记return，则数据还是原来的。
		}
		// echo json_encode($data); 
		return $data;
		
	}///get
	
	

	
    /**
     * @example:  exportExcel($datas,$fieldsTitle,$fields,$colFormat=array();
     * @description: 导出excel表格，传入的数据必须是格式标准的行列数据，从数据库select的就行
     * @param {array}} [datas] [数据，是二维数组，第一维由每一行数据构成的关联数组构成的普通数组，第二维则是每一行数据构成的关联数组]
     * @param {array}} [fieldsTitle] [普通数组，表格第一行，的字段title，列名]
     * @param {array}} [fields] [普通数组，传入的数据的字段名，与title对应。必须是datas中存在的，可以少于datas的字段。]
     * @param {array}} [colFormat] [关联数组，键值对：列名和可选的string字符串，用来格式化某一行的数据为字符串。默认是空数组。]
     * @return: {none} example: 
     */
    public function exportExcel($datas,$fieldsTitle,$fields,$colFormat=array(),$filename='export'){
        $this->load->helper('numtoletter');//自封装的数字转字母序号
			// StartingthePHPExcellibrary
		$this -> load -> library('PHPExcel');
		$this -> load -> library('PHPExcel/IOFactory');
		$objPHPExcel = new PHPExcel();
		$objPHPExcel -> getProperties() -> setTitle("export") -> setDescription("none");
		$objPHPExcel -> setActiveSheetIndex(0);
		
		
		// 列
		$col = 0;
	
		$ffff = $fieldsTitle;
		foreach($ffff as $field)
		{
			$objPHPExcel -> getActiveSheet() -> setCellValueByColumnAndRow($col, 1, $field);
			$col++;
		}
			// Fetchingthetabledata
		$row = 2;
		foreach($datas as $data)
			{
				$col = 0;
				foreach($fields as $field)
				{
					
						if(isset($colFormat[num_to_letter($col)]) && $colFormat[num_to_letter($col)] === 'string'){//设置了某一列的格式化为字符串
							$objPHPExcel->getActiveSheet()->setCellValueExplicit(num_to_letter($col).$row,$data[$field],PHPExcel_Cell_DataType::TYPE_STRING);
						}else{
							$objPHPExcel -> getActiveSheet() -> setCellValueByColumnAndRow($col, $row, $data[$field]);
						}
			
				$col++;
			}
			$row++;
		}

		$objPHPExcel -> setActiveSheetIndex(0);
		$objWriter = IOFactory :: createWriter($objPHPExcel, 'Excel5');
				// Sendingheaderstoforcetheusertodownloadthefile

				// $objWriter -> save('./files/123.xls');//保存在服务器上
				// return;
				// 下载生成的文件数据
		date_default_timezone_set('PRC');
		header('Content-Type:application/vnd.ms-excel');
		header('Content-Disposition:attachment;filename="'.$filename.'.xls"');
		header('Cache-Control:max-age=0');
		$objWriter -> save('php://output');
	}





}///class
